/*
 * $Id: Database.java 28 2009-04-02 21:58:37Z aaron.day $
 */
package org.crazydays.gameplan.data;


import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.util.Set;
import java.util.TreeSet;
import java.util.zip.ZipOutputStream;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.crazydays.gameplan.map.IGameMap;

/**
 * Database
 */
public class Database
        implements Serializable
{
	/** serial version uid */
	public final static long serialVersionUID =
	        "$Id: Database.java 28 2009-04-02 21:58:37Z aaron.day $"
	                .hashCode();

	/** log */
	protected static Log LOG = LogFactory.getLog(Database.class);

	/** file */
	protected File file;

	/** maps */
	protected Set<IGameMap> gameMaps = new TreeSet<IGameMap>();

	/**
	 * Database constructor.
	 */
	public Database()
	{
	}

	/**
	 * Database constructor.
	 * 
	 * @param file File
	 */
	public Database(File file)
	{
		setFile(file);
	}

	/**
	 * Set file.
	 * 
	 * @param file File
	 */
	public void setFile(File file)
	{
		if (file == null) {
			throw new IllegalArgumentException("file == null");
		}

		this.file = file;
	}

	/**
	 * Get file.
	 * 
	 * @return File
	 */
	public File getFile()
	{
		return file;
	}

	/**
	 * Get game maps.
	 * 
	 * @return GameMaps
	 */
	public Set<IGameMap> getGameMaps()
	{
		return gameMaps;
	}

	/**
	 * Add game map.
	 * 
	 * @param gameMap GameMap
	 */
	public void addGameMap(IGameMap gameMap)
	{
		getGameMaps().add(gameMap);
	}

	/**
	 * Load database from input stream.
	 * 
	 * @param inputStream InputStream
	 */
	public void load(InputStream inputStream)
	{
		// TODO: implement me!
		throw new UnsupportedOperationException("Load not implemented!");
	}

	/**
	 * Save database to disk.
	 * 
	 * @throws IOException
	 */
	public void save()
	        throws IOException
	{
		save(getFile());
	}

	/**
	 * Auto save file to disk.
	 * 
	 * @throws IOException
	 */
	public void autoSave()
	        throws IOException
	{
		File autoSave = null;
		File file = getFile();

		if (file != null) {
			autoSave =
			        new File(file.getParentFile(), file.getName()
			                + "~");
		} else {
			autoSave = new File("gameplan.gp1~");
		}

		save(autoSave);
	}

	/**
	 * Save file to disk.
	 * 
	 * @param file File
	 * @throws IOException
	 */
	protected synchronized void save(File file)
	        throws IOException
	{
		if (file == null) {
			throw new IllegalArgumentException("file == null");
		}

		File saveFile =
		        new File(file.getParentFile(), file.getName() + "~");

		if (saveFile.exists() && !saveFile.delete()) {
			LOG.warn("Unable to delete saveFile: " + saveFile);
			throw new IOException("Unable to overwrite: "
			        + saveFile);
		}

		ZipOutputStream zipOutputStream =
		        new ZipOutputStream(new FileOutputStream(saveFile));
		try {
			saveGameMaps(zipOutputStream);
		} finally {
			zipOutputStream.close();
		}

		// rename tempfile to file
		if (file.exists() == false
		        || (file.exists() == true && file.delete() == true)) {

			if (saveFile.renameTo(file) == false) {
				LOG.warn("Unable to rename to file: " + file);
				throw new IOException("Unable to file: " + file);
			}
		} else {
			LOG.warn("Unable to delete file: " + file);
			throw new IOException("Unable to overwrite: " + file);
		}
	}

	/**
	 * Save game maps.
	 * 
	 * @param zipOutputStream ZipOutputStream
	 * @throws IOException
	 */
	protected void saveGameMaps(ZipOutputStream zipOutputStream)
	        throws IOException
	{
		if (zipOutputStream == null) {
			throw new IllegalArgumentException(
			        "zipOutputStream == null");
		}

		for (IGameMap map : getGameMaps()) {
			GameMapIO.save(map, zipOutputStream);
		}
	}
}
